package com.duguo.dynamicmvc.factory.processor.controller;

import java.lang.reflect.Modifier;
import java.util.Map;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import com.duguo.dynamicmvc.controller.RootController;
import com.duguo.dynamicmvc.factory.ControllerFactory;
import com.duguo.dynamicmvc.factory.WorkingModel;
import com.duguo.dynamicmvc.factory.utils.BeanUtils;
import com.duguo.dynamicmvc.factory.utils.javassist.MethodUtils;
import com.duguo.dynamicmvc.view.View;


public class ControllerClassCreatorImpl implements ControllerClassCreator{
	
	ControllerMethodCreator _controllerMethodCreator;
	ControllerNameBuilder _controllerNameBuilder;

    public void setupDynamicControllerName(WorkingModel workingModel) throws Exception {
        int parentLevel = _controllerNameBuilder.getControllerLevel(workingModel.getControllerPath());
        for (String controllerName : workingModel.getControllerBuilder().getControllerFactoryBeans().keySet()) {
            if (controllerName.startsWith(workingModel.getControllerPath())) {
                int childLevel = _controllerNameBuilder.getControllerLevel(controllerName);
                if (childLevel == (parentLevel + 1)) {
                    Object tempControllerFactory = workingModel.getControllerBuilder().getControllerFactoryBeans().get(controllerName);
                    Class controllerClass = BeanUtils.getFieldValue(tempControllerFactory, "objectType", Class.class);
                    CtClass controllerAbstractCtClass = ClassPool.getDefault().get(controllerClass.getName());
                    CtMethod dynamicControllerCtMethod = workingModel.getControllerBuilder().getProcessorHolder().getControllerProcessor().findDynamicMethod(controllerAbstractCtClass);
                    if (dynamicControllerCtMethod != null) {
                        workingModel.setDynamicControllerName(controllerName);
                        return;
                    }
                }
            }
        }
    }


    public ControllerFactory getParentControllerFactory(WorkingModel workingModel) {
        int childLevel = _controllerNameBuilder.getControllerLevel(workingModel.getControllerPath());
        Map<String, ControllerFactory> controllerFactoryBeans = workingModel.getControllerFactory().getControllerBuilder().getControllerFactoryBeans();
        for (int i = childLevel - 1; i > 0; i--) {
            for (String controllerName : controllerFactoryBeans.keySet()) {
                int parentLevel = _controllerNameBuilder.getControllerLevel(controllerName);
                if (parentLevel == i && workingModel.getControllerPath().startsWith(controllerName)) {
                    return controllerFactoryBeans.get(controllerName);
                }
            }
        }
        return null;
    }


    public void setupControllerClass(WorkingModel workingModel) throws Exception {
        
        CtClass controllerAbstractCtClass = ClassPool.getDefault().get(workingModel.getControllerClass().getName());
        String controllerName;
        if(workingModel.isRootController()){
        	controllerName=_controllerNameBuilder.getControllerInternalRootClassName();
        }else if(workingModel.isMultiActionController()){
        	controllerName=_controllerNameBuilder.getControllerInternalClassName(_controllerNameBuilder.getControllerInternalRootClassName(),workingModel.getControllerPath(), true);        	
        }else if(workingModel.isSingleActionController()){
        	controllerName=_controllerNameBuilder.getControllerInternalClassName(_controllerNameBuilder.getControllerInternalRootClassName(),workingModel.getControllerPath()+"index/", false);        	
        }else{
        	controllerName=_controllerNameBuilder.getControllerInternalClassName(_controllerNameBuilder.getControllerInternalRootClassName(),workingModel.getControllerPath(), false);        	
        }
        CtClass controllerCtClass = ClassPool.getDefault().makeClass(controllerName);
        controllerCtClass.setSuperclass(controllerAbstractCtClass);
        if (workingModel.isRootController()) {
            setupRootControllerInterface(controllerCtClass);
        }
        workingModel.setControllerCtClass(controllerCtClass);

        setupControllerMethod(workingModel);
        setupInternalHandlerInterfaceCtClass(workingModel);
    }


    public void setupControllerMethod(WorkingModel workModel) throws Exception {
        CtMethod tempCtMethod;
        if(workModel.isRootController()){
        	tempCtMethod = MethodUtils.findMethodFromInterface(workModel.getControllerCtClass(), "handleRequest");
        }else{
        	tempCtMethod=workModel.getParentWorkModel().getControllerHandlerCtMethod();
        }
	    if(workModel.isDynamicController()){
	    	 CtMethod dynamicControllerCtMethod = workModel.getControllerBuilder().getProcessorHolder().getControllerProcessor().findDynamicMethod(workModel.getControllerCtClass());
	    	 tempCtMethod=MethodUtils.addParameterToMethod(workModel.getControllerCtClass(),tempCtMethod,dynamicControllerCtMethod.getReturnType());
	    }
        workModel.setControllerHandlerCtMethod(tempCtMethod);
    }


    public void setupInternalHandlerInterfaceCtClass(WorkingModel workCacheModel) throws Exception {
        CtClass internalHandlerInterfaceCtClass = _controllerMethodCreator.getInternalInterface(workCacheModel.getControllerHandlerCtMethod());
        workCacheModel.setInternalHandlerInterfaceCtClass(internalHandlerInterfaceCtClass);
        if(!workCacheModel.isRootController()){
        	workCacheModel.getControllerCtClass().addInterface(internalHandlerInterfaceCtClass);
        }
    }


    void setupRootControllerInterface(CtClass controllerCtClass) throws Exception {
        CtClass rootControllerInterface = ClassPool.getDefault().get(RootController.class.getName());
        controllerCtClass.addInterface(rootControllerInterface);
    }


	public boolean isControllerMethod(CtMethod controllerMethodCtMethod) throws Exception {
		if(isOneParamsControllerMethod(controllerMethodCtMethod)) {
			return true;
		}else
			return isTwoParamsControllerMethod(controllerMethodCtMethod);
    }



	public boolean isOneParamsControllerMethod(CtMethod controllerMethodCtMethod) throws Exception {		
        return controllerMethodCtMethod.getReturnType() == CtClass.voidType
                && Modifier.isPublic(controllerMethodCtMethod.getModifiers())
                && controllerMethodCtMethod.getParameterTypes().length==1
                && !_controllerNameBuilder.isSetter(controllerMethodCtMethod.getName())
                && !controllerMethodCtMethod.getParameterTypes()[0].isPrimitive();
    }

	public boolean isTwoParamsControllerMethod(CtMethod controllerMethodCtMethod) throws Exception {		
        return controllerMethodCtMethod.getReturnType() == getViewCtClass()
                && Modifier.isPublic(controllerMethodCtMethod.getModifiers())
                && isParameterModelAndView(controllerMethodCtMethod);
    }

    boolean isParameterModelAndView(CtMethod controllerMethodCtMethod) throws Exception {
        CtClass[] params = controllerMethodCtMethod.getParameterTypes();
        if (params.length == 2) {
            if (params[1] == getViewCtClass()) {
                return true;
            }
        }
        return false;
    }

    public CtClass getViewCtClass() throws Exception {
        return ClassPool.getDefault().get(View.class.getName());
    }


	public ControllerMethodCreator getControllerMethodCreator() {
		return _controllerMethodCreator;
	}


	public void setControllerMethodCreator(
			ControllerMethodCreator controllerMethodCreator) {
		_controllerMethodCreator = controllerMethodCreator;
	}


	public ControllerNameBuilder getControllerNameBuilder() {
		return _controllerNameBuilder;
	}


	public void setControllerNameBuilder(ControllerNameBuilder controllerNameBuilder) {
		_controllerNameBuilder = controllerNameBuilder;
	}
}
